<?php
namespace Swork\Validator;

use Swork\Bean\Annotation\Validate;
use Swork\Exception\ValidateException;

/**
 * 检查规则器
 * Class Regulation
 * @package Swork\Server\Validator
 */
class Regulation
{
    /**
     * 支持必填的叠加规则
     * @var array
     */
    private static $supportRequired = [
        'Number', 'Letter', 'LetterNumber', 'TextKey', 'NotZero',
        'Email', 'Mobile', 'Url',
        'Month', 'Date', 'Time', 'DateTime',
        'Decimal', 'Digits',
        'GreaterZero', 'GreaterEqualZero', 'LessZero', 'LessEqualZero'
    ];

    /**
     * 检查值是为空
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function required(string $name, $val, array $call)
    {
        if ($val == '')
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]不能为空";
            }
            throw new ValidateException($message, Validate::Required);
        }
        $match = $call['match'] ?? '';
        if ($match != '')
        {
            if (!in_array($match, self::$supportRequired))
            {
                throw new ValidateException("Required不支持叠加[$match]", Validate::Required);
            }
            $target = $call['target'];
            $void = $call['match'];
            $target::$void($name, $val, $call);
        }
    }

    /**
     * 检查值是否为数字
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function number(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match('/^\d+$/', $val))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]不是纯0-9的数字";
            }
            throw new ValidateException($message, Validate::Number);
        }
    }

    /**
     * 检查值是否为字母（不分大小写）
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function letter(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match('/^[a-zA-Z]+$/', $val))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]不是纯字母";
            }
            throw new ValidateException($message, Validate::Letter);
        }
    }

    /**
     * 检查值是否为字母（不分大小写）
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function letterNumber(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match('/^[a-zA-Z0-9]+$/', $val))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]不是字母数字";
            }
            throw new ValidateException($message, Validate::LetterNumber);
        }
    }

    /**
     * 检查值是否为不含特殊字段的文本
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function textKey(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match('/^[A-Za-z0-9_\-,]+$/', $val))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]不是合法文本";
            }
            throw new ValidateException($message, Validate::TextKey);
        }
    }

    /**
     * 检查值是否为不等于0
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function notZero(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match('/^\d+$/', $val))
        {
            throw new ValidateException("[$name]不能包含非数字字符", Validate::Letter);
        }
        if ($val == 0)
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]不能等于0";
            }
            throw new ValidateException($message, Validate::NotZero);
        }
    }

    /**
     * 检查邮箱格式
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function email(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!filter_var($val, FILTER_VALIDATE_EMAIL))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]邮箱格式不正确";
            }
            throw new ValidateException($message, Validate::Email);
        }
    }

    /**
     * 检查手机号码格式
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function mobile(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match('/^1([356789]\d{9}|47\d{8})$/', $val))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]手机号码不正确";
            }
            throw new ValidateException($message, Validate::Mobile);
        }
    }

    /**
     * 检查http:// 或 https:// 格式
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function url(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!filter_var($val, FILTER_VALIDATE_URL))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]url格式不正确";
            }
            throw new ValidateException($message, Validate::Url);
        }
    }

    /**
     * 检查YYYY-MM 的格式
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function month(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        $message = $call['message'];
        if ($message == '')
        {
            $message = "[$name]日期格式不是有效的月份";
        }
        //匹配日期格式
        if (preg_match("/^([0-9]{4})-([0-9]{2})$/", $val, $parts))
        {
            //检测是否为日期
            if (!checkdate($parts[2], 1, $parts[1]))
            {
                throw new ValidateException($message, Validate::Month);
            }
        }
        else
        {
            throw new ValidateException($message, Validate::Month);
        }
    }

    /**
     * 检查YYYY-MM-DD 的格式
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function date(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        $message = $call['message'];
        if ($message == '')
        {
            $message = "[$name]日期格式不是有效的格利高里日期";
        }
        //匹配日期格式
        if (preg_match("/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/", $val, $parts))
        {
            //检测是否为日期
            if (!checkdate($parts[2], $parts[3], $parts[1]))
            {
                throw new ValidateException($message, Validate::Date);
            }
        }
        else
        {
            throw new ValidateException($message, Validate::Date);
        }
    }

    /**
     * 检查hh:mm(:ss) 的格式
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function time(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match("/^([0-1]\d|2[0-4]):([0-5]\d)(:[0-5]\d)?$/", $val))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]时间格式不正确hh:mm(:ss)";
            }
            throw new ValidateException($message, Validate::Time);
        }
    }

    /**
     * 检查YYYY-MM-DD hh:mm(:ss) 的格式
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function dateTime(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match("/^([12]\d\d\d)-(0?[1-9]|1[0-2])-(0?[1-9]|[12]\d|3[0-1]) ([0-1]\d|2[0-4]):([0-5]\d)(:[0-5]\d)?$/", $val))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]日期时间格式不正确YYYY-MM-DD hh:mm(:ss)";
            }
            throw new ValidateException($message, Validate::DateTime);
        }
    }

    /**
     * 检查小数（含正负数）
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function decimal(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match("/^-?\d+\.\d+$/", $val))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]不是浮点数";
            }
            throw new ValidateException($message, Validate::Decimal);
        }
    }

    /**
     * 整数（含正负数，不以0开头的数字）
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function digits(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match("/^-?\d+$/", $val))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]不是整数";
            }
            throw new ValidateException($message, Validate::Digits);
        }
    }

    /**
     * 检验 两个字段的值是否相同
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function equal(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        $val2 = $call['val2'];
        if ($val != $val2)
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]和[{$call['match'][1]}]值不相同";
            }
            throw new ValidateException($message, Validate::Equal);
        }
    }

    /**
     * 检验 前面字段是否大于后面字段，或固定数值
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function greater(string $name, $val, array $call)
    {
        $val2 = $call['val2'];
        if ($val == '' || $val2 == '')
        {
            return;
        }
        if ($val <= $val2)
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]应该大于[{$call['match'][1]}]";
            }
            throw new ValidateException($message, Validate::Greater);
        }
    }

    /**
     * 检验 前面字段是否大于等于后面字段，或固定数值
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function greaterEqual(string $name, $val, array $call)
    {
        $val2 = $call['val2'];
        if ($val == '' || $val2 == '')
        {
            return;
        }
        if ($val < $val2)
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]应该大于或等于[{$call['match'][1]}]";
            }
            throw new ValidateException($message, Validate::GreaterEqual);
        }
    }

    /**
     * 检验 前面字段是否小于后面字段，或固定数值
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function lesser(string $name, $val, array $call)
    {
        $val2 = $call['val2'];
        if ($val == '' || $val2 == '')
        {
            return;
        }
        if ($val >= $val2)
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]应该小于[{$call['match'][1]}]";
            }
            throw new ValidateException($message, Validate::Lesser);
        }
    }

    /**
     * 检验 前面字段是否小于等于后面字段，或固定数值
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function lesserEqual(string $name, $val, array $call)
    {
        $val2 = $call['val2'];
        if ($val == '' || $val2 == '')
        {
            return;
        }
        if ($val > $val2)
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]应该小于等于[{$call['match'][1]}]";
            }
            throw new ValidateException($message, Validate::LesserEqual);
        }
    }

    /**
     * 检查值是否为大于0
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function greaterZero(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match('/^\-?\d+$/', $val))
        {
            throw new ValidateException("[$name]不能包含非数字字符", Validate::Letter);
        }
        if ($val <= 0)
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]需要大于0";
            }
            throw new ValidateException($message, Validate::GreaterZero);
        }
    }

    /**
     * 检查值是否为大于等于0
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function greaterEqualZero(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match('/^\-?\d+$/', $val))
        {
            throw new ValidateException("[$name]不能包含非数字字符", Validate::Letter);
        }
        if ($val < 0)
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]需要大于等于0";
            }
            throw new ValidateException($message, Validate::GreaterEqualZero);
        }
    }

    /**
     * 检查值是否为小于0
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function lessZero(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match('/^\-?\d+$/', $val))
        {
            throw new ValidateException("[$name]不能包含非数字字符", Validate::Letter);
        }
        if ($val >= 0)
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]需要小于0";
            }
            throw new ValidateException($message, Validate::LessZero);
        }
    }

    /**
     * 检查值是否为小于等于0
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function lessEqualZero(string $name, $val, array $call)
    {
        if ($val == '')
        {
            return;
        }
        if (!preg_match('/^\-?\d+$/', $val))
        {
            throw new ValidateException("[$name]不能包含非数字字符", Validate::Letter);
        }
        if ($val > 0)
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]需要小于等于0";
            }
            throw new ValidateException($message, Validate::LessEqualZero);
        }
    }

    /**
     * 检查值是否在指定范围内
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws
     */
    public static function ins(string $name, $val, array $call)
    {
        $vals = $call['match'];
        if (!in_array($val, $vals))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]不在指定范围内";
            }
            throw new ValidateException($message, Validate::Ins);
        }
    }

    /**
     * 检查数组值长度
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws ValidateException
     */
    public static function count(string $name, $val, array $call)
    {
        //如果有空，则不检查
        if ($val == '')
        {
            return;
        }

        //提取长度参数
        $vals = $call['match'];
        $count = count($vals);
        if ($count == 0)
        {
            return;
        }

        $message = $call['message'];

        //判断是不是数组类型
        if (!is_array($val))
        {
            throw new ValidateException("[$name]不是数组类型", Validate::Count);
        }

        $length = count($val);

        $msg = "[$name]数量应该";
        $flg = true;
        if ($count == 1 && $length != $vals[0])
        {
            $flg = false;
            $msg .= '等于' . $vals[0];
        }
        else
        {
            if ($vals[0] != '' && $length < $vals[0])
            {
                $flg = false;
                $msg .= '大于' . $vals[0];
            }
            if ($vals[1] != '' && $length > $vals[1])
            {
                $flg = false;
                $msg .= '小于' . $vals[1];
            }
        }

        if (!$flg)
        {
            $message = $message == '' ? $msg : $message;
            throw new ValidateException($message, Validate::Count);
        }
    }

    /**
     * 检查字符值长度
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws ValidateException
     */
    public static function length(string $name, $val, array $call)
    {
        //如果有空，则不检查
        if ($val == '')
        {
            return;
        }

        //提取长度参数
        $vals = $call['match'];
        $count = count($vals);
        if ($count == 0)
        {
            return;
        }

        $message = $call['message'];
        $length = mb_strlen($val);

        $msg = "[$name]长度应该";
        $flg = true;
        if ($count == 1 && $length != $vals[0])
        {
            $flg = false;
            $msg .= '等于' . $vals[0];
        }
        else
        {
            if ($vals[0] != '' && $length < $vals[0])
            {
                $flg = false;
                $msg .= '大于' . $vals[0];
            }
            if ($vals[1] != '' && $length > $vals[1])
            {
                $flg = false;
                $msg .= '小于' . $vals[1];
            }
        }

        if (!$flg)
        {
            $message = $message == '' ? $msg : $message;
            throw new ValidateException($message, Validate::Length);
        }
    }

    /**
     * 检查值范围
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws ValidateException
     */
    public static function range(string $name, $val, array $call)
    {
        //如果有空，则不检查
        if ($val == '')
        {
            return;
        }

        $vals = $call['match'];
        $count = count($vals);
        if (!is_numeric($val))
        {
            throw new ValidateException("[$name]值不是数字", Validate::Range);
        }
        if ($count == 0)
        {
            return;
        }
        $msg = "[$name]值应该";
        $flg = true;
        if ($count == 1 && $val != $vals[0])
        {
            $flg = false;
            $msg .= '等于' . $vals[0];
        }
        else
        {
            if ($vals[0] != '' && $val < $vals[0])
            {
                $flg = false;
                $msg .= '大于' . $vals[0];
            }
            if ($vals[1] != '' && $val > $vals[1])
            {
                $flg = false;
                $msg .= '小于' . $vals[1];
            }
        }

        if (!$flg)
        {
            $message = $call['message'];
            $message = $message == '' ? $msg : $message;
            throw new ValidateException($message, Validate::Range);
        }
    }

    /**
     * 检查满足正则表达式
     * @param string $name 目标检查字段
     * @param string|int $val 待检查的值
     * @param array $call 检查规划
     * @throws ValidateException
     */
    public static function regex(string $name, $val, array $call)
    {
        //如果有空，则不检查
        if ($val == '')
        {
            return;
        }

        //提取正则表达式
        $rule = $call['match'];

        //判断是否符合
        if (!preg_match("/$rule/", $val))
        {
            $message = $call['message'];
            if ($message == '')
            {
                $message = "[$name]不符合规则要求";
            }
            throw new ValidateException($message, Validate::Ins);
        }
    }
}
